package com.ibm.cps.sql.expression;

import java.io.IOException;
import java.util.HashSet;
import java.util.Set;

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonSerializable;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.jsontype.TypeSerializer;
import com.ibm.cps.sql.expression.json.CalculationNodeDeserializer;
import com.ibm.util.ErrorCode;
import com.ibm.util.exception.CPSException;

@JsonDeserialize(using = CalculationNodeDeserializer.class)
public class CalculationNode implements ValueExpression, RecursiveExpression, JsonSerializable {
	private Operator operator;
	private ValueExpression leftOperand;
	private ValueExpression rightOperand;
	
	public CalculationNode() {
		//Only used for serialization
	}

	public CalculationNode(Operator operator, ValueExpression leftOperand,
			ValueExpression rightOperand) {
		this.operator = operator;
		this.leftOperand = leftOperand;
		this.rightOperand = rightOperand;
	}

	@Override
	public String toString() {
		return "{\"CalculationNode\":{\"operator\":\"" + operator
				+ "\", \"leftOperand\":\"" + leftOperand
				+ "\", \"rightOperand\":\"" + rightOperand + "\"}}";
	}

	@Override
	public Set<String> collectVariableNames() {
		Set<String> result = new HashSet<>();
		result.addAll(leftOperand.collectVariableNames());
		result.addAll(rightOperand.collectVariableNames());
		return result;
	}

	public Operator getOperator() {
		return operator;
	}

	public ExpressionNode getLeftOperand() {
		return leftOperand;
	}

	public ExpressionNode getRightOperand() {
		return rightOperand;
	}
	
	public void setOperator(Operator operator) {
		this.operator = operator;
	}

	public void setLeftOperand(ValueExpression leftOperand) {
		this.leftOperand = leftOperand;
	}

	public void setRightOperand(ValueExpression rightOperand) {
		this.rightOperand = rightOperand;
	}

	@Override
	public ExpressionNode[] getChildNodes() {
		return new ExpressionNode[] { leftOperand, rightOperand };
	}

	@Override
	public Object evaluate(FieldEvaluator evaluator) throws CPSException {
		try {
			Number leftValue = (Number) leftOperand.evaluate(evaluator);
			Number rightValue = (Number) rightOperand.evaluate(evaluator);
			switch (operator) {
			case PLUS:
				return leftValue.doubleValue() + rightValue.doubleValue();
			case MINUS:
				return leftValue.doubleValue() - rightValue.doubleValue();
			case TIMES:
				return leftValue.doubleValue() * rightValue.doubleValue();
			default:
				return leftValue.doubleValue() / rightValue.doubleValue();
			}
		} catch (Exception ex) {
			throw new CPSException(ErrorCode.INTERNAL_ERROR_CODE,
					"Exception occured when executing calculation node "
							+ toString() + " values = " + evaluator.toString(), ex);
		}
	}

	@Override
	public void updateVariableName(String original, String fullQualifiedName) {
		leftOperand.updateVariableName(original, fullQualifiedName);
		rightOperand.updateVariableName(original, fullQualifiedName);
	}
	
	public static enum Operator {
		PLUS, MINUS, TIMES, DIVIDE
	}

	@Override
	public void serialize(JsonGenerator jgen, SerializerProvider arg1)
			throws IOException, JsonProcessingException {
		jgen.writeStartObject();
		jgen.writeStringField("operator", operator.toString());
		jgen.writeStringField("leftOperandType", leftOperand.getClass().getName());
		jgen.writeObjectField("leftOperand", leftOperand);
		jgen.writeStringField("rightOperandType", rightOperand.getClass().getName());
		jgen.writeObjectField("rightOperand", rightOperand);
		jgen.writeEndObject();
	}

	@Override
	public void serializeWithType(JsonGenerator arg0, SerializerProvider arg1,
			TypeSerializer arg2) throws IOException, JsonProcessingException {
	}
}
